fix(integrations): stop installed runtime configs drifting silently - #107
Conversation
pi and Hermes could not see the sentinel server, three days after it shipped and with CI green throughout. Both read an installed *copy* of their config, and the drift guards only ever checked the templates in this repo — a distinction with no symptom until an operator asks a runtime for a tool that isn't there. `sync_pi_config.py` rendered the template wholesale over `~/.pi/agent/mcp.json`. A live pi install commonly carries servers from elsewhere — this machine has `f0-library` from the sibling repo — so the documented remedy for a missing server would have silently deleted a different one, leaving only a .bak the operator has no reason to read. It now merges: entries this repo ships always win, anything else is preserved. The Hermes distribution manifest still described six platforms and pinned version 0.2.0, while its config wired all nine. `mcp_servers` completeness was already guarded across every template; the sentence a human reads before installing was not, so it drifted twice without failing anything. Two guards added — the description must name every platform under servers/, and the manifest version must match the repo's. The Hermes guide gains the update step it never had: an installed profile is a copy, `hermes profile install` must be re-run after a pull, and there is a one-liner to diff installed against shipped without installing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XYTy7da8Z5ZHhkwcCpjojZ
|
Claude finished @ubercylon8's task in 2m 12s —— View job Security review — PR #107
Scope note: this PR touches only local dev tooling ( No secrets or credentials are handled by this code — One correctness/robustness observation (non-blocking, not a vulnerability):
Everything else checks out:
Note: I couldn't execute |
…te membership Review of #107 caught that treating "absent from the template" as "foreign" only solves the drift in one direction. A server this repo renamed or removed would be indistinguishable from a sibling repo's entry and preserved forever, pointing at a command that no longer exists — a zombie instead of a deletion, but the same silent drift the merge was added to prevent. Ownership is now decided by whether an entry references this checkout. A stale entry of ours is dropped; another repo's is untouched. Verified against the live pi install, where f0-library points at the sibling checkout and survives. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XYTy7da8Z5ZHhkwcCpjojZ
|
Took the review's observation rather than deferring it — It is right that "absent from the template" only solves the drift in one direction: a server we renamed or removed would be indistinguishable from a sibling repo's entry and preserved forever, pointing at a command that no longer exists. A zombie instead of a deletion, but the same silent drift this merge exists to prevent — and this session has already spent a while on exactly that class of bug. Ownership is now decided by whether an entry references this checkout, not by whether the template still names it. A stale entry of ours is dropped; another repo's is untouched. Verified against the live pi install: Mutation-tested by reverting to name-only ownership — the new test fails as it should. 1084 passing. |
|
Claude finished @ubercylon8's task in 1m 12s —— View job Security review — PR #107 (re-run on latest push)
Scope note (unchanged from the last pass on this PR): this diff touches only local dev tooling ( No secrets or credentials are handled by this code. Follow-up on the earlier finding — now fixed. My previous pass on this PR flagged that deciding "foreign" purely by Minor, non-blocking observation: the marker match is a plain substring check on New drift guards checked out:
Nothing else stood out. I was not able to execute |
pi and Hermes could not see the sentinel server — three days after it shipped, with CI green the whole time.
Why CI never noticed
Both runtimes read an installed copy of their config, outside the repo. The drift guards in
integrations/test_integrations_valid.pyverify that every template in this repo lists every server — and they did, correctly, for all nine. Nothing checks what is actually installed, and nothing can: it lives in~/.pi/and~/.hermes/. The distinction has no symptom until an operator asks a runtime for a tool that isn't there.Measured on this machine:
~/.pi/agent/mcp.jsonwas last written three days before sentinel merged, and the installed Hermes profile a day before that.The trap in the documented remedy
sync_pi_config.pyrendered the template wholesale over~/.pi/agent/mcp.json. A live pi install commonly carries servers from other checkouts — this machine hasf0-libraryfrom the sibling repo — so running the documented fix for a missing server would have silently deleted a different one, leaving only a.bakthe operator has no reason to open.It now merges: entries this repo ships always win (a stale command line must be refreshed), anything else is preserved untouched.
Stale manifest, guarded now
The Hermes distribution manifest described six platforms and pinned
version: 0.2.0while itsconfig.yamlwired all nine.mcp_serverscompleteness was already guarded across every template; the sentence a human reads before installing was not, so it drifted twice without failing anything.Two guards added:
servers/Docs
The Hermes guide never had an update step. It now says plainly that an installed profile is a copy, that
hermes profile installmust be re-run after a pull, that Hermes reports no error when a server is simply absent, and gives a one-liner to diff installed against shipped without installing.Verification
1083 tests passing, ruff and mypy clean, no
gen_docsdrift. Two mutations run against the merge logic, both caught.Applied to this machine as well: pi now lists all nine servers plus
f0-library(preserved), and the installed Hermes profile matches the shipped distribution exactly.🤖 Generated with Claude Code
https://claude.ai/code/session_01XYTy7da8Z5ZHhkwcCpjojZ